package com.engine.jucailinkq.attendance.workflow.service.impl;

import com.engine.jucailinkq.attendance.enums.AccountingUnitEnum;
import com.engine.jucailinkq.attendance.enums.AttendanceItemTypeEnum;
import com.engine.jucailinkq.attendance.enums.ClassBelongToEnum;
import com.engine.jucailinkq.attendance.enums.ClassSegmentTypeEnum;
import com.engine.jucailinkq.attendance.workflow.service.AllowanceService;
import com.engine.jucailinkq.common.util.CommonUtil;
import com.engine.jucailinkq.common.util.DateUtil;
import com.engine.jucailinkq.common.util.DbTools;
import com.engine.jucailinkq.common.util.Utils;
import com.engine.core.impl.Service;
import lombok.extern.slf4j.Slf4j;
import weaver.conn.RecordSet;
import weaver.general.BaseBean;
import weaver.general.Util;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: sy
 * @Description: 出勤津贴
 * @Date: 2024/4/2
 **/
@Slf4j
public class AllowanceServiceImpl extends Service implements AllowanceService {
    BaseBean bs = new BaseBean();
    @Override
    public Map<String, Object> addAllowanceRecords(Map<String, Object> params) {

        Map<String, Object> result = new HashMap<>();
        try {
            //获取指定生成的人员id列表，如果没有则生成所有人
            String checkEmpIds = Util.null2String(params.get("empIds"));
            List<String> checkEmpIdList = new ArrayList<>();
            if (!checkEmpIds.equals("")) {
                checkEmpIdList = Arrays.asList(checkEmpIds.split(","));
            }
            //获取modeId，考勤方案的modeId
            Map<String,String> formmodeIdMap = Utils.getFormmodeIdMap();
            String modeId = formmodeIdMap.get("uf_jcl_kq_kqfa");
            //查询考勤方案
            String sql = "select * from uf_jcl_kq_kqfa where zt = 0";
            List<Map<String,Object>> data = DbTools.getSqlToList(sql);
            //获取通用津贴类型考勤项目
            List<String> tyAllowanceIds = new ArrayList<>();
            String tyAllowanceSql = "select * from uf_jcl_kq_kqxm where tyxm = 1 and xmlx = 7";
            List<Map<String,Object>> tyAllowanceData = DbTools.getSqlToList(tyAllowanceSql);
            for (Map<String, Object> map : tyAllowanceData) {
                tyAllowanceIds.add(map.get("id").toString());
            }
//            log.debug("tyAllowanceIds : " +  tyAllowanceIds);
            //获取人员和津贴类型考勤项目信息，构建映射关系
            Map<String, List<String>> empIdWithAllowanceInfo = new HashMap<>();
            List<String> targetEmpIdList = new ArrayList<>();
            List<String> targetAllowanceIdList = new ArrayList<>();
            List<String> targetBcIdList = new ArrayList<>();
            if (tyAllowanceIds.size() > 0) {
                targetAllowanceIdList.addAll(tyAllowanceIds);
            }
            for (Map<String, Object> map : data) {
                String dataId = map.get("id").toString();
                //获取方案适用的人员id列表
                List<String> empIdList = CommonUtil.getEmpIds(dataId, modeId);
                //获取津贴类型的考勤项目
                String dtSql = "select * from uf_jcl_kq_kqfa_dt1 a left join uf_jcl_kq_kqxm b on b.id = a.kqxm where a.mainid = " + dataId
                        + " and b.xmlx = " + AttendanceItemTypeEnum.ALLOWANCE.getKey();
                List<Map<String,Object>> dtData = DbTools.getSqlToList(dtSql);
                List<String> allowanceIdList = new ArrayList<>();
                dtData.forEach(f -> allowanceIdList.add(f.get("kqxm").toString()));
                if (allowanceIdList.size() > 0 && empIdList.size() > 0) {
                    targetEmpIdList.addAll(empIdList);
                    targetAllowanceIdList.addAll(allowanceIdList);
                    //构建映射
                    empIdList.forEach(f -> {
                        List<String> valueIdList = new ArrayList<>();
                        if (empIdWithAllowanceInfo.get(f) == null) {
                            empIdWithAllowanceInfo.put(f, allowanceIdList);
                            if (tyAllowanceIds.size() > 0) {
                                valueIdList.addAll(allowanceIdList);
                                valueIdList.addAll(tyAllowanceIds);
                                empIdWithAllowanceInfo.put(f, valueIdList);
                            }
                        } else {
                            valueIdList = empIdWithAllowanceInfo.get(f);
                            valueIdList.addAll(allowanceIdList);
                            empIdWithAllowanceInfo.put(f, valueIdList);
                        }
                    });
                }
            }
            //20240515产品需求变更，即使考勤方案未设置适用范围，对于通用津贴类型考勤项目，也要适用于全员
            if (tyAllowanceIds.size() > 0) {
                String empSql = "select id from hrmresource where 1=1 ";
                if (targetEmpIdList.size() > 0) {
                    empSql = empSql + " and id not in (" + String.join(",",targetEmpIdList) + ")";
                }
                List<Map<String, Object>> empData = DbTools.getSqlToList(empSql);
                for (Map<String, Object> empInfo : empData) {
                    targetEmpIdList.add(empInfo.get("id").toString());
                    empIdWithAllowanceInfo.put(empInfo.get("id").toString(), tyAllowanceIds);
                }
            }
            //构建班次和津贴映射关系
            Map<String, Map<String, Object>> allowanceIdWithKqxmInfo = new HashMap<>();
            Map<String, List<String>> bcWithAllowanceInfo = new HashMap<>();
            if (targetAllowanceIdList.size() > 0) {
                //去重
                targetAllowanceIdList = targetAllowanceIdList.stream().distinct().collect(Collectors.toList());
                //构建映射
                String allowanceWithBcSql = "select * from uf_jcl_kq_kqxm where id in ("+String.join(",",targetAllowanceIdList) + ") and sydbc is not null ";
                List<Map<String, Object>> allowanceWithBcData = DbTools.getSqlToList(allowanceWithBcSql);
                for (Map<String, Object> map : allowanceWithBcData) {
                    String bcArr = map.get("sydbc").toString();
                    String allowanceId = map.get("id").toString();
                    allowanceIdWithKqxmInfo.put(allowanceId, map);
                    List<String> bcList = Arrays.asList(bcArr.split(","));
                    targetBcIdList.addAll(bcList);
                    bcList.forEach(f -> {
                        List<String> values = new ArrayList<>();
                        if (bcWithAllowanceInfo.get(f) == null) {
                            values.add(allowanceId);
                            bcWithAllowanceInfo.put(f, values);
                        } else {
                            values = bcWithAllowanceInfo.get(f);
                            values.add(allowanceId);
                            bcWithAllowanceInfo.put(f, values);
                        }
                    });
                }
            }
            //获取出勤结果，遍历处理津贴信息
            String startDate = Util.null2String(params.get("startDate"));
            String endDate = Util.null2String(params.get("endDate"));
            List<Map<String, Object>> cqData = new ArrayList<>();
            //如果入参中选中了人员，则只处理选中的人
            if (checkEmpIdList.size() > 0) {
                List<String> finalCheckEmpIdList = checkEmpIdList;
                targetEmpIdList = targetEmpIdList.stream().filter(f -> finalCheckEmpIdList.contains(f)).collect(Collectors.toList());
            }
            if (targetEmpIdList.size() > 0 && targetBcIdList.size() > 0) {
                //去重
                targetBcIdList = targetBcIdList.stream().distinct().collect(Collectors.toList());
                //查询
                String cqSql = "select * from uf_jcl_kq_cqjg where ygid in ("+String.join(",",targetEmpIdList) + ") and rq >= '" + startDate +"'" + " and rq <= '" + endDate +"'"
                        + " and bc in ("+String.join(",",targetBcIdList) + ")";
                cqData = DbTools.getSqlToList(cqSql);
            }
            log.debug("cqData_size : " +  cqData.size());
            //获取待新增的津贴数据
            List<Map<String, String>> allowanceInfoList = createAllowanceInfo(cqData, empIdWithAllowanceInfo, bcWithAllowanceInfo, allowanceIdWithKqxmInfo);
            log.debug("allowanceInfoList_size : " +  allowanceInfoList.size());
            //新增津贴数据，先删后加
            if (targetEmpIdList.size() > 0 && targetBcIdList.size() > 0) {
                String delSql = "delete from uf_jcl_kq_cqjt where rq >= '" + startDate +"'" + " and rq <= '" + endDate +"'"
                        + " and yg in (" + String.join(",",targetEmpIdList) + ") and bc in (" + String.join(",",targetBcIdList) + ")";
                boolean delSign = DbTools.update(delSql);
                log.debug("delSign : " +  delSign + ", delSql : " + delSql);
            }
            if (allowanceInfoList.size() > 0) {
                //插入津贴数据
                List<List> dataList = new ArrayList<>();
                String insertSql = " insert into uf_jcl_kq_cqjt(formmodeid,modeuuid,modedatacreater,modedatacreatertype,modedatacreatedate,modedatacreatetime," +
                        "szjg,yg,rq,bc,jtlx,sc,zt) values(?,?,?,?,?,?,?,?,?,?,?,?,?)";
                String formmodeId = formmodeIdMap.get("uf_jcl_kq_cqjt");
                for (Map<String, String> map : allowanceInfoList) {
                    List<String> list = new ArrayList<>();

                    list.add(formmodeId);
                    list.add(UUID.randomUUID().toString());
                    list.add("1");
                    list.add("0");
                    list.add(DateUtil.getCurrentTime().split(" ")[0]);
                    list.add(DateUtil.getCurrentTime().split(" ")[1]);
                    list.add(map.get("szjg"));
                    list.add(map.get("yg"));
                    list.add(map.get("rq"));
                    list.add(map.get("bc"));
                    list.add(map.get("jtlx"));
                    list.add(map.get("sc"));
                    list.add(map.get("zt"));

                    dataList.add(list);
                }
                log.debug("dataList_size : " +  dataList.size());
                if(dataList.size() > 0){
                    log.debug("津贴数据记录入库开始---");
                    boolean insertSign = DbTools.updateBatch(insertSql,dataList);
                    log.debug("insertSign : " + insertSign);
                    result.put("result", insertSign);
                }
            }

        } catch (Exception e) {
            bs.errorLog("catch error :{}",e);
        }
        return result;
    }

    private List<Map<String, String>> createAllowanceInfo(List<Map<String, Object>> cqData, Map<String, List<String>> empIdWithAllowanceInfo, Map<String
            , List<String>> bcWithAllowanceInfo, Map<String, Map<String, Object>> allowanceIdWithKqxmInfo) {
        List<Map<String, String>> allowanceInfoList = new ArrayList<>();
        Map<String, String> allowanceInfoItem;
        //获取加班类型的考勤项目，并构建项目id和核算单位映射关系
        Map<String, String> jbKqxmUnitInfo = collectKqxmUnitInfo(AttendanceItemTypeEnum.WORK_OVERTIME.getKey());
        //获取津贴类型的考勤项目，并构建项目id和核算单位映射关系
        Map<String, String> jtKqxmUnitInfo = collectKqxmUnitInfo(AttendanceItemTypeEnum.ALLOWANCE.getKey());
        //获取津贴类型的考勤项目，并构建项目id和核算量映射关系
        Map<String, String> jtKqxmCountScaleInfo = collectKqxmCountScaleInfo(AttendanceItemTypeEnum.ALLOWANCE.getKey());
        //获取班次对应的班段明细数据
        Map<String, List<Map<String, Object>>> shiftIdToDetailInfo = collectShiftIdToDetailInfo();
        //获取班次对应的开始时间
        Map<String, String> shiftIdToStartInfo = collectShiftIdToStartInfo();
        //遍历出勤结果
        for (Map<String, Object> map : cqData) {
            String empId = map.get("ygid").toString();
            String bc = Util.null2String(map.get("bc"));
            //判断该班次是否存在津贴
            List<String> allowanceIdsByBc = bcWithAllowanceInfo.get(bc);
            //判断该人员是否有相关的津贴
            List<String> allowanceIdsByEmp = empIdWithAllowanceInfo.get(empId);
            //处理该人员在该班次可生效的津贴
            if(allowanceIdsByBc != null && allowanceIdsByEmp != null) {
                //取交集
                allowanceIdsByBc = allowanceIdsByBc.stream()
                        .filter(allowanceIdsByEmp::contains)
                        .collect(Collectors.toList());
                if (allowanceIdsByBc.size() == 0) {
                    continue;
                }
                //收集下出勤结果中项目1-8存在的项目id
                List<String> kqxmIdsWithCq = collectKqxmIdsByCq(map);
                //收集下出勤结果中进出时间，仅收集进出两个时间点都有的数据
                List<Map<String, String>> jcInfoList = collectJcInfoByCq(map);
                //出勤日期
                String cqRq = Util.null2String(map.get("rq"));
//                //收集下出勤结果中，项目1-8中累积的加班时长，处理成分钟数
//                Integer realOvertimeMinutes = collectOvertimeMinutes(map, jbKqxmUnitInfo);
                //20240703需求变更，收集加班结果列表中的加班时长
                List<Map<String, String>> realOvertimeInfoList = collectOvertimeInfo(empId, cqRq, jbKqxmUnitInfo);
                int realOvertimeMinutes = realOvertimeInfoList.stream().mapToInt(e->Integer.parseInt(e.get("scMinutes"))).sum();
                log.debug("realOvertimeMinutes : " +  realOvertimeMinutes);
                //最终核算分钟数
                double countMinutes = 0;
                //出勤时长
                double cqsc = Util.null2String(map.get("cqsc")).equals("") ? 0 : Double.parseDouble(map.get("cqsc").toString());

                //获取班次班段时间范围
                List<Map<String, String>> bcTimeRangeList = collectBcTimeRangeInfo(shiftIdToDetailInfo.get(bc), cqRq);
                //遍历该出勤结果中班次、人员都关联的津贴项目
                for(String allowanceId : allowanceIdsByBc) {
                    Integer overtimeMinutes = realOvertimeMinutes;
                    Map<String, Object> allowanceInfo = allowanceIdWithKqxmInfo.get(allowanceId);
                    //项目1-8中出现以下考勤项目时，津贴失效
                    String banKqxm = Util.null2String(allowanceInfo.get("bxsdxm"));
                    List<String> banKqxmList = new ArrayList<>();
                    if (!banKqxm.equals("")) {
                        banKqxmList = Arrays.asList(banKqxm.split(","));
                    }
                    //是否需要审核标识，,0-否，1-是
                    String checkSign = Util.null2String(allowanceInfo.get("sfxysh"));
                    //正常工作时长计入核算量_标识,0-否，1-是
                    String includeDailyWorkHours = Util.null2String(allowanceInfo.get("ljbcngzsj"));
                    //加班工作时长计入核算量_标识,0-否，1-是
                    String includeOverWorkHours = Util.null2String(allowanceInfo.get("jbscjrhsl"));
                    //20240620需求变更，勾选了加班时长计入核算量时，才将加班时长算入核算时长
                    if (!includeOverWorkHours.equals("1")) {
                        //结合打卡统计加班时长不能超过出勤结果中项目1-8的加班时长这一逻辑，在未勾选加班时长计入核算量时，直接将项目1-8中累积的加班时长置为0
                        overtimeMinutes = 0;
                    }
                    //检查特定时间点是否在岗_标识,0-否，1-是
                    String checkOneTimeOnDuty = Util.null2String(allowanceInfo.get("xysjd"));
                    //特定在岗时间点
                    String onDutyTime = Util.null2String(allowanceInfo.get("sjd"));
                    //判断特定在岗时间点指向日期
                    String onDutyDate = getOnDutyDate(shiftIdToStartInfo.get(bc), onDutyTime, cqRq);
                    //20240704需求变更，jljxysjdzqdsc字段为“累计时间点前时长”，ljsjdhdsc字段为“累计时间点后时长”
//                    //时间点之前的时长也计入核算量_标识,0-否，1-是
////                    String includeHoursBeforeDutyTime = Util.null2String(allowanceInfo.get("jljxysjdzqdsc"));
                    //累计时间点前时长计入核算量_标识,0-否，1-是
                    String onlyCountBeforeDutyTime = Util.null2String(allowanceInfo.get("jljxysjdzqdsc"));
                    //累计时间点后时长计入核算量_标识,0-否，1-是
                    String onlyCountAfterDutyTime = Util.null2String(allowanceInfo.get("ljsjdhdsc"));
                    //起算分钟数(不包含)
                    String startMinutes = Util.null2String(allowanceInfo.get("jtqsfzs"));
                    //最大核算分钟数（包含）
                    String maxMinutes = Util.null2String(allowanceInfo.get("zdhsfzs"));
                    //超出起算时长的单次累加分钟数
                    String oneTimeAddMinutes = Util.null2String(allowanceInfo.get("cchsfzs"));
                    log.debug("includeDailyWorkHours : " +  includeDailyWorkHours + ",checkOneTimeOnDuty : " +  checkOneTimeOnDuty
                                    + ",onDutyTime : " +  onDutyTime + ",onlyCountBeforeDutyTime : " +  onlyCountBeforeDutyTime + ",onlyCountAfterDutyTime : " +  onlyCountAfterDutyTime
                                    + ",startMinutes : " +  startMinutes,"maxMinutes : " +  maxMinutes + ",oneTimeAddMinutes : " +  oneTimeAddMinutes);
                    //逐一判断是否满足津贴时长计入规则
                    //1-此为考勤项目的多选，如果员工当天出勤中的项目1~项目8存在不核算此津贴的项目，则员工不能获得此津贴
                    //取交集
                    banKqxmList = banKqxmList.stream()
                            .filter(kqxmIdsWithCq::contains)
                            .collect(Collectors.toList());
                    if (banKqxmList.size() > 0) {
                        continue;
                    }
                    //2-如果勾选“检查特定时间点是否在岗”，则需要判断员工当天“须要在岗的时间点”所设置的时间员工是否在岗
                    if (checkOneTimeOnDuty.equals("1") && !onDutyTime.equals("")) {
                        boolean onDuty = false;
                        //将特殊在岗时间点拼接上日期
                        String onDutyTimeWithDate = onDutyDate + " " + onDutyTime;
                        for (Map<String, String> jcInfo : jcInfoList) {
                            if (onDutyTimeWithDate.compareTo(jcInfo.get("signIn")) >= 0 && onDutyTimeWithDate.compareTo(jcInfo.get("signOut")) <= 0) {
                                onDuty = true;
                                break;
                            }
                        }
                        //不在岗，直接处理下一个津贴
                        if (!onDuty) {
                            continue;
                        }
                        //3-如果时长大于“起算分钟数（不包含）”小于等于“最大核算分钟数（包含）”，则员工或获得此津贴
                        //a-时长默认为取当天所有项目1~项目8中的各加班类型的考勤项目对应的时长
                        //b-如果勾选“工作时长也计入核算量”，则还需要累加当天的“出勤时长”字段值
                        //c-一旦勾选了“检查特定时间点是否在岗”，且只取“时间点”之后的分钟数时长
                        //d-如果“累计时间点前时长”和“累计时间点后时长”同时勾选
                        if ("1".equals(onlyCountBeforeDutyTime) && "1".equals(onlyCountAfterDutyTime)) {
                            countMinutes = overtimeMinutes;
                            if ("1".equals(includeDailyWorkHours)) {
                                countMinutes = countMinutes + cqsc * 60;
                            }
                        } else if(!"1".equals(onlyCountBeforeDutyTime) && !"1".equals(onlyCountAfterDutyTime)) {
                            countMinutes = 0;
                        } else {
                            countMinutes = getMinutesWithAfterPointTime(onlyCountBeforeDutyTime, onlyCountAfterDutyTime, bcTimeRangeList, jcInfoList,realOvertimeInfoList, onDutyTimeWithDate, includeDailyWorkHours);
                        }
                    } else {
                        //未勾选“检查特定时间点是否在岗”
                        countMinutes = overtimeMinutes;
                        if ("1".equals(includeDailyWorkHours)) {
                            countMinutes = countMinutes + cqsc * 60;
                        }
                    }
                    //4-津贴时长计算规则
                    //a-起算分钟数 + 超出起算时长的单次累加分钟数 * N <= 最大核算分钟数
                    //b-由a点获取到分钟数后，要根据津贴项目的核算量+核算单位来获取最终津贴时长，例如分钟数170分钟，核算量为0.5，核算单位为小时，则最终核算时长为2.5小时
                    double allowanceMinutes = 0;
                    log.debug("countMinutes : " +  countMinutes);
                    if (countMinutes > Double.parseDouble(startMinutes)) {
                        if (!"".equals(oneTimeAddMinutes) && !"0".equals(oneTimeAddMinutes)) {
                            double multiple = (countMinutes - Double.parseDouble(startMinutes)) / Double.parseDouble(oneTimeAddMinutes);
                            allowanceMinutes = Double.parseDouble(startMinutes) + Double.parseDouble(oneTimeAddMinutes) * Math.floor(multiple);
                        } else {
                            allowanceMinutes = Double.parseDouble(startMinutes);
                        }
                        if (allowanceMinutes > Double.parseDouble(maxMinutes)) {
                            allowanceMinutes = Double.parseDouble(maxMinutes);
                        }
                        log.debug("allowanceMinutes : " +  allowanceMinutes);
                        //津贴时长
                        String allowanceUnit = Util.null2String(jtKqxmUnitInfo.get(allowanceId));
                        String countScale = Util.null2String(jtKqxmCountScaleInfo.get(allowanceId));
                        if (!countScale.equals("")) {
                            double sc = 0;
                            if (allowanceUnit.equals(AccountingUnitEnum.DAY.getKey())) {
                                sc = (allowanceMinutes - allowanceMinutes % (1440 * Double.parseDouble(countScale))) / 1440;
                            } else if (allowanceUnit.equals(AccountingUnitEnum.HOUR.getKey())) {
                                sc = (allowanceMinutes - allowanceMinutes % (60 * Double.parseDouble(countScale))) / 60;
                            } else if (allowanceUnit.equals(AccountingUnitEnum.MINUTES.getKey())) {
                                sc = allowanceMinutes - allowanceMinutes % Double.parseDouble(countScale);
                            }
                            log.debug("sc : " +  sc);
                            //记录待新增的津贴记录
                            allowanceInfoItem = new HashMap<>();
                            allowanceInfoItem.put("szjg", Util.null2String(map.get("szjg")));
                            allowanceInfoItem.put("yg", Util.null2String(map.get("ygid")));
                            allowanceInfoItem.put("rq", cqRq);
                            allowanceInfoItem.put("bc", Util.null2String(map.get("bc")));
                            allowanceInfoItem.put("jtlx", allowanceId);
                            //不需要审核的话，直接就是已审核状态
                            allowanceInfoItem.put("zt", !"1".equals(checkSign) ? "1" : "0");
                            if (!allowanceUnit.equals(AccountingUnitEnum.ONCE.getKey())) {
                                allowanceInfoItem.put("sc", String.format("%.2f", sc));
                            } else {
                                allowanceInfoItem.put("sc", "1");
                            }
                            allowanceInfoList.add(allowanceInfoItem);
                        }
                    }
                }
            }
        }
        return allowanceInfoList;
    }
    //收集加班结果列表中的加班时长信息列表
    private List<Map<String, String>> collectOvertimeInfo(String empId, String cqRq, Map<String, String> jbKqxmUnitInfo) {
        List<Map<String, String>> overtimeInfo = new ArrayList<>();
        String sql = "select * from uf_jcl_kq_jbjg where zt = 1 and jbry = " + empId + " and (jbgzrq = '" + cqRq + "' or sjksrq = '" + cqRq + "')";
        List<Map<String, Object>> data = DbTools.getSqlToList(sql);
        data = data.stream().
                filter(f -> Util.null2String(f.get("jbgzrq")).equals(cqRq)
                        || ("".equals(Util.null2String(f.get("jbgzrq"))) && Util.null2String(f.get("sjksrq")).equals(cqRq)))
                .collect(Collectors.toList());
        Map<String, String> overtimeInfoItem;
        Map<String, Integer> unitToMinutes = new HashMap<>();
        unitToMinutes.put(AccountingUnitEnum.DAY.getKey(), 1440);
        unitToMinutes.put(AccountingUnitEnum.HOUR.getKey(), 60);
        unitToMinutes.put(AccountingUnitEnum.MINUTES.getKey(), 1);
        unitToMinutes.put(AccountingUnitEnum.ONCE.getKey(), 0);
        String unit;
        int minutes;

        for (Map<String, Object> item : data) {
            String startDate = Util.null2String(item.get("sjksrq"));
            String endDate = Util.null2String(item.get("sjjsrq"));
            String startTime = Util.null2String(item.get("sjkssj"));
            String endTime = Util.null2String(item.get("sjjssj"));
            String kqxmId = Util.null2String(item.get("jblx"));
            String scStr = Util.null2String(item.get("sjjbsc"));
            double sc = "".equals(scStr) ? 0 : Double.parseDouble(scStr);
            if (!"".equals(startDate) && !"".equals(endDate) && !"".equals(startTime) && !"".equals(endTime) && !"".equals(kqxmId) && sc > 0) {
                overtimeInfoItem = new HashMap<>();
                overtimeInfoItem.put("startTime", startDate + " " + startTime);
                overtimeInfoItem.put("endTime", endDate + " " + endTime);
                unit = Util.null2String(jbKqxmUnitInfo.get(kqxmId));
                minutes = 0;
                if (!"".equals(unit)) {
                    minutes = (int) (sc * unitToMinutes.get(unit));
                }
                overtimeInfoItem.put("scMinutes", String.valueOf(minutes));
                overtimeInfo.add(overtimeInfoItem);
            }
        }
        return overtimeInfo;
    }



    private Map<String, String> collectShiftIdToStartInfo() {
        Map<String, String> shiftIdToStartInfo = new HashMap<>();
        String sql = "select * from uf_jcl_kq_bcxx";
        List<Map<String, Object>> data = DbTools.getSqlToList(sql);
        for (Map<String, Object> item : data) {
            shiftIdToStartInfo.put(item.get("id").toString(), item.get("kssj").toString());
        }
        return shiftIdToStartInfo;
    }

    /**
     * 获取班次明细表中班段类型为工作时段的数据，并映射成key为班次id的对象
     * @return
     */
    private Map<String, List<Map<String, Object>>> collectShiftIdToDetailInfo() {
        Map<String, List<Map<String, Object>>> shiftIdToDetailInfo = new HashMap<>();
        String sql = "select * from uf_jcl_kq_bcxx_dt1 where bdlx = " + ClassSegmentTypeEnum.WORK_TIME.getKey();
        List<Map<String, Object>> detailList = DbTools.getSqlToList(sql);
        if (detailList.size() > 0) {
            shiftIdToDetailInfo = detailList.stream().collect(Collectors.groupingBy(
                            map -> (String) map.get("mainid"),
                            Collectors.mapping(map -> map, Collectors.toList())));
        }
        return shiftIdToDetailInfo;

    }


    /**
     * 特定在岗时间点指向日期
     * @param bcStartTime 班次开始时间
     * @param onDutyTime 特殊在岗时间点
     * @param cqRq 出勤结果中出勤日期
     * @return
     */
    private String getOnDutyDate(String bcStartTime, String onDutyTime, String cqRq) {
        boolean currentDateSign = false;
        if ("".equals(onDutyTime) || onDutyTime.compareTo(bcStartTime) >= 0) {
            currentDateSign = true;
        }
        if (currentDateSign) {
            return cqRq;
        } else {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 时间格式
            LocalDate date = LocalDate.parse(cqRq, formatter); // 解析为LocalDate对象
            LocalDate nextDay = date.plusDays(1); // 将日期加一天
            return nextDay.format(formatter);
        }
    }

    /**
     * 获取考虑特殊在岗时间点情况下的考核时长分钟数
     * 勾选在岗时间点时，
     * 1-处于班次工作时间段时，结合进出时间和班次班段判断班段时间内实际工作分钟数
     * 2-处于加班时间段内，结合加班结果中的进出时间记录，累加
     *
     * @param onlyCountBeforeDutyTime
     * @param onlyCountAfterDutyTime
     * @param onDutyTimeWithDate
     * @return
     */
    private double getMinutesWithAfterPointTime(String onlyCountBeforeDutyTime, String onlyCountAfterDutyTime, List<Map<String, String>> bcTimeRangeList, List<Map<String, String>> jcInfoList,
                                                List<Map<String, String>> realOvertimeInfoList, String onDutyTimeWithDate, String includeDailyWorkHours) {
        double bcOnDutyMinutes = 0;
        double realOverWorkMinutes = 0;
        if (includeDailyWorkHours.equals("1")) {
            //判断在岗时间点和班次班段的关系，存在包含关系，则计算出班次时间内需要累计的时间
            //首先获取班次班段设置和实际进出交集的时间段
            List<Map<String, String>> bcRealOnDutyTimeRange = new ArrayList<>();
            Map<String, String> realItemMap;
            for (Map<String, String> bcTime : bcTimeRangeList) {
                String realStartTime = "";
                String realEndTime = "";
                //取班次开始和结束时间
                String startTime = bcTime.get("startTime");
                String endTime = bcTime.get("endTime");
                for (Map<String, String> jcTime : jcInfoList) {
                    String signInTime = jcTime.get("signIn");
                    String signOutTime = jcTime.get("signOut");
                    if (startTime.compareTo(signInTime) >= 0 && startTime.compareTo(signOutTime) <= 0) {
                        realStartTime = startTime;
                    }
                    if (endTime.compareTo(signInTime) >= 0 && endTime.compareTo(signOutTime) <= 0) {
                        realEndTime = endTime;
                    }
                    if (startTime.compareTo(signInTime) < 0 && endTime.compareTo(signOutTime) > 0) {
                        realStartTime = signInTime;
                        realEndTime = signOutTime;
                    }
                    if (realStartTime.equals("") && !realEndTime.equals("")) {
                        realStartTime = signInTime;
                    }
                    if (!realStartTime.equals("") && realEndTime.equals("")) {
                        realEndTime = signOutTime;
                    }
                    if (!realStartTime.equals("") && !realEndTime.equals("")) {
                        realItemMap = new HashMap<>();
                        realItemMap.put("startTime", realStartTime);
                        realItemMap.put("endTime", realEndTime);
                        bcRealOnDutyTimeRange.add(realItemMap);
                    }
                }
            }
            if ("1".equals(onlyCountBeforeDutyTime)) {
                //遍历获取班次班段时间内的累计时长（某一在岗时间点之前的时间）
                for (Map<String, String> bcRealTimeInfo : bcRealOnDutyTimeRange) {
                    if (onDutyTimeWithDate.compareTo(bcRealTimeInfo.get("endTime")) >= 0) {
                        bcOnDutyMinutes = bcOnDutyMinutes + getMinutesByTwo(bcRealTimeInfo.get("startTime"), bcRealTimeInfo.get("endTime"));
                    } else if (onDutyTimeWithDate.compareTo(bcRealTimeInfo.get("startTime")) >= 0 && onDutyTimeWithDate.compareTo(bcRealTimeInfo.get("endTime")) <= 0) {
                        bcOnDutyMinutes = bcOnDutyMinutes + getMinutesByTwo(bcRealTimeInfo.get("startTime"), onDutyTimeWithDate);
                    }
                }
            } else if ("1".equals(onlyCountAfterDutyTime)) {
                //遍历获取班次班段时间内的累计时长（某一在岗时间点之后的时间）
                for (Map<String, String> bcRealTimeInfo : bcRealOnDutyTimeRange) {
                    if (onDutyTimeWithDate.compareTo(bcRealTimeInfo.get("startTime")) <= 0) {
                        bcOnDutyMinutes = bcOnDutyMinutes + getMinutesByTwo(bcRealTimeInfo.get("startTime"), bcRealTimeInfo.get("endTime"));
                    } else if (onDutyTimeWithDate.compareTo(bcRealTimeInfo.get("startTime")) >= 0 && onDutyTimeWithDate.compareTo(bcRealTimeInfo.get("endTime")) <= 0) {
                        bcOnDutyMinutes = bcOnDutyMinutes + getMinutesByTwo(onDutyTimeWithDate, bcRealTimeInfo.get("endTime"));
                    }
                }
            }
        }

        if ("1".equals(onlyCountBeforeDutyTime)) {
            //判断在岗时间点和加班时间区间的关系，即加班结果表中的进出记录是否与在岗时间点有包含关系，分情况累计某一在岗时间点之前的时间
            for (Map<String, String> jcInfo : realOvertimeInfoList) {
                String scStr = Util.null2String(jcInfo.get("scMinutes"));
                int scMinutes = Integer.parseInt(scStr);
                if (onDutyTimeWithDate.compareTo(jcInfo.get("endTime")) >= 0) {
                    realOverWorkMinutes = realOverWorkMinutes + scMinutes;
                } else if (onDutyTimeWithDate.compareTo(jcInfo.get("startTime")) >= 0 && onDutyTimeWithDate.compareTo(jcInfo.get("endTime")) <= 0) {
                    realOverWorkMinutes = realOverWorkMinutes + Math.min(getMinutesByTwo(jcInfo.get("startTime"), onDutyTimeWithDate), scMinutes);
                }
            }
        } else if ("1".equals(onlyCountAfterDutyTime)) {
            //判断在岗时间点和加班时间区间的关系，即加班结果表中的进出记录是否与在岗时间点有包含关系，分情况累计某一在岗时间点之后的时间
            for (Map<String, String> jcInfo : realOvertimeInfoList) {
                String scStr = Util.null2String(jcInfo.get("scMinutes"));
                int scMinutes = Integer.parseInt(scStr);
                if (onDutyTimeWithDate.compareTo(jcInfo.get("startTime")) <= 0) {
                    realOverWorkMinutes = realOverWorkMinutes + scMinutes;
                } else if (onDutyTimeWithDate.compareTo(jcInfo.get("startTime")) >= 0 && onDutyTimeWithDate.compareTo(jcInfo.get("endTime")) <= 0) {
                    realOverWorkMinutes = realOverWorkMinutes + Math.min(getMinutesByTwo(onDutyTimeWithDate, jcInfo.get("endTime")), scMinutes);
                }
            }
        }
        return realOverWorkMinutes + bcOnDutyMinutes;
    }

    private double getMinutesByTwo(String startTime, String endTime) {
        List<String> startInfoList = Arrays.asList(startTime.split("\\s+|-|:"));
        List<String> endInfoList = Arrays.asList(endTime.split("\\s+|-|:"));

        LocalDateTime dateTime1 = LocalDateTime.of(Integer.parseInt(startInfoList.get(0)),
                Integer.parseInt(replaceFirstChar(startInfoList.get(1),"0")),
                Integer.parseInt(replaceFirstChar(startInfoList.get(2),"0")),
                Integer.parseInt(replaceFirstChar(startInfoList.get(3),"0")),
                Integer.parseInt(replaceFirstChar(startInfoList.get(4),"0")));
        LocalDateTime dateTime2  = LocalDateTime.of(Integer.parseInt(endInfoList.get(0)),
                Integer.parseInt(replaceFirstChar(endInfoList.get(1),"0")),
                Integer.parseInt(replaceFirstChar(endInfoList.get(2),"0")),
                Integer.parseInt(replaceFirstChar(endInfoList.get(3),"0")),
                Integer.parseInt(replaceFirstChar(endInfoList.get(4),"0")));

        Duration duration = Duration.between(dateTime1, dateTime2);
        // 获取分钟数
        return duration.toMinutes();
    }
    public static String replaceFirstChar(String original, String target) {
        int index = original.indexOf(target);
        if ("00".equals(original) || index == -1) {
            return original; // 如果没有找到目标字符，返回原字符串
        } else if (index == 0) {
            return original.substring(1);
        } else {
            return original;
        }
    }

    /**
     * 根据班次班段详细数据中
     * 班段类型、归属日、班段开始结束时间获取班段中多个年月日时分的时间点
     * @return
     */
    private List<Map<String, String>> collectBcTimeRangeInfo(List<Map<String, Object>> bcDetailList, String cqRq) {
        List<Map<String, String>> timeRangeInfo = new ArrayList<>();
//        if (!bcbdxx.equals("")) {
//            List<String> rangeList = Arrays.asList(bcbdxx.split("\\|"));
//            Map<String, String> itemMap;
//            for (String range : rangeList) {
//                String[] timeInfo = range.split("-");
//                itemMap = new HashMap<>();
//                itemMap.put("startTime", timeInfo[0]);
//                itemMap.put("endTime", timeInfo[1]);
//                timeRangeInfo.add(itemMap);
//            }
//        }
        if (bcDetailList != null && bcDetailList.size() > 0) {
            String bcDate = "";
            Map<String, String> itemMap;
            for (Map<String, Object> detailItem : bcDetailList) {
                bcDate = cqRq;
                String belongDayType = Util.null2String(detailItem.get("gsrq"));
                String startTime = Util.null2String(detailItem.get("kssj"));
                String endTime = Util.null2String(detailItem.get("jssj"));
                if (belongDayType.equals(ClassBelongToEnum.YESTERDAY.getKey())) {
                    bcDate = DateUtil.beforeDay(bcDate,1);
                } else if (belongDayType.equals(ClassBelongToEnum.NEXTDAY.getKey())) {
                    bcDate = DateUtil.AfterDay(bcDate,1);
                }
                if (!"".equals(startTime) && !"".equals(endTime)) {
                    itemMap = new HashMap<>();
                    itemMap.put("startTime", bcDate + " " + startTime);
                    if (startTime.compareTo(endTime) >= 0) {
                        itemMap.put("endTime", DateUtil.AfterDay(bcDate,1) + " " + endTime);
                    } else {
                        itemMap.put("endTime", bcDate + " " + endTime);
                    }
                    timeRangeInfo.add(itemMap);
                }
            }
        }
        return timeRangeInfo;
    }

    private Map<String, String> collectKqxmUnitInfo(String kqxmType) {
        Map<String, String> unitInfo = new HashMap<>();
        String sql = "select * from uf_jcl_kq_kqxm where xmlx = " + kqxmType;
        List<Map<String,Object>> data = DbTools.getSqlToList(sql);
        for (Map<String, Object> map : data) {
            unitInfo.put(map.get("id").toString(), Util.null2String(map.get("hsdw")));
        }
        return unitInfo;
    }

    private Map<String, String> collectKqxmCountScaleInfo(String kqxmType) {
        Map<String, String> unitInfo = new HashMap<>();
        String sql = "select * from uf_jcl_kq_kqxm where xmlx = " + kqxmType;
        List<Map<String,Object>> data = DbTools.getSqlToList(sql);
        for (Map<String, Object> map : data) {
            unitInfo.put(map.get("id").toString(), Util.null2String(map.get("hsl")));
        }
        return unitInfo;
    }

    //收集下出勤结果中，项目1-8中累积的加班时长，处理成分钟数
    private Integer collectOvertimeMinutes(Map<String, Object> cqMap, Map<String, String> jbKqxmUnitInfo) {
        double minutes = 0;
        String xm = "";
        String sc = "";
        String unit = "";
        Map<String, Integer> unitToMinutes = new HashMap<>();
        unitToMinutes.put(AccountingUnitEnum.DAY.getKey(), 1440);
        unitToMinutes.put(AccountingUnitEnum.HOUR.getKey(), 60);
        unitToMinutes.put(AccountingUnitEnum.MINUTES.getKey(), 1);
        unitToMinutes.put(AccountingUnitEnum.ONCE.getKey(), 0);
        for (int i = 1; i <= 8; i++) {
           xm = Util.null2String(cqMap.get("xm" + i));
           sc = Util.null2String(cqMap.get("sc" + i));
           unit = Util.null2String(jbKqxmUnitInfo.get(xm));
           if (!xm.equals("") && !sc.equals("") && !unit.equals("")) {
               minutes = minutes + Double.parseDouble(sc) * unitToMinutes.get(unit);
           }
        }
        return (int) Math.round(minutes);
    }

    private List<Map<String, String>> collectJcInfoByCq(Map<String, Object> cqMap) {
        List<Map<String, String>> jcInfoList = new ArrayList<>();
        String singIn = "";
        String signOut = "";
        Map<String, String> itemMap;
        for (int i = 1; i <= 8; i++) {
            singIn = Util.null2String(cqMap.get("j" + i));
            signOut = Util.null2String(cqMap.get("c" + i));
            if (!singIn.equals("") && !signOut.equals("")) {
                itemMap = new HashMap<>();
                itemMap.put("signIn", singIn.substring(0, 16));
                itemMap.put("signOut", signOut.substring(0, 16));
                jcInfoList.add(itemMap);
            } else if (singIn.equals("") && signOut.equals("")){
                break;
            }
        }
        return jcInfoList;
    }

    private List<String> collectKqxmIdsByCq(Map<String, Object> cqMap) {
        List<String> kqxmIdsWithCq = new ArrayList<>();
        String xmId = "";
        for (int i = 1; i <= 8; i++) {
            xmId = Util.null2String(cqMap.get("xm" + i));
            if (!xmId.equals("")) {
                kqxmIdsWithCq.add(xmId);
            } else {
                break;
            }
        }
        return kqxmIdsWithCq;

    }

    @Override
    public Map<String, Object> getAllowanceRecordList(Map<String, Object> params) {
        Map<String, Object> resultMap = new HashMap<String, Object>();
        resultMap.put("status", true);
        // 流程表单主表数据
        Map<String,String> mainTableData = (Map<String,String>)params.get("mainTableData");
        String targetUserId = Util.null2String(mainTableData.get("xjr"));
        String queryDateRange = Util.null2String(mainTableData.get("cxqj"));
        String startDate = "";
        String endDate = "";
        String targetMonth = "";
        if ("0".equals(queryDateRange)) {
            //上月
            targetMonth = DateUtil.beforeMonth(DateUtil.getCurrentDate());
        } else if ("1".equals(queryDateRange)) {
            targetMonth = DateUtil.getCurrentMonth();
        }
        startDate = targetMonth + "-01";
        //结束日期只作为范围筛选，不用考虑是目标月份是否存在31号
        endDate = targetMonth + "-31";
        log.debug("targetUserId : {}, queryDateRange : {}, startDate : {}, endDate : {}", targetUserId, queryDateRange, startDate, endDate);
        String detailSql = "select a.id as jtid, a.rq, a.bc, a.jtlx, a.sc, a.zt , a.bz as sm, c.mc as jtlxName, d.mc as bcName, b.bcbdxx, b.cqzt, b.id as cqjg, " +
                "b.j1, b.c1, b.j2, b.c2, b.j3, b.c3, b.j4, b.c4, b.j5, b.c5, b.j6, b.c6, b.j7, b.c7, b.j8, b.c8 from uf_jcl_kq_cqjt a " +
                "left join uf_jcl_kq_cqjg b on a.yg = b.ygid and a.rq = b.rq " +
                "left join uf_jcl_kq_kqxm c on a.jtlx = c.id " +
                "left join uf_jcl_kq_bcxx d on a.bc = d.id " +
                "where a.yg = ? and a.rq >= ? and a.rq <= ? ";
        List<Map<String, Object>> detailList = DbTools.getSqlToList(detailSql,targetUserId,startDate,endDate);
        if (detailList.size() > 0) {
            String in = "";
            String out = "";
            String yxdkxx;
            for (Map<String, Object> item : detailList) {
                yxdkxx = "";
                for (int i = 1; i <= 8; i++) {
                    in = Util.null2String(item.get("j" + i));
                    out = Util.null2String(item.get("c" + i));
                    yxdkxx = "".equals(in) && "".equals(out) ? yxdkxx : (yxdkxx + getHourMinuteInfo(in) + "-" + getHourMinuteInfo(out) + "|");
                }
                item.put("yxdkxx", yxdkxx);
                item.put("cqjgSign", "0".equals(Util.null2String(item.get("cqzt"))) ? "正常" : "异常");
            }
            resultMap.put("data", detailList);
        } else {
            resultMap.put("data", null);
        }
        log.debug("detailList : [{}]", detailList);
        return resultMap;
    }

    //将2024-06-11 07:54:00格式数据，保留时分数据07：54
    private String getHourMinuteInfo(String timeInfo) {
        if (!"".equals(timeInfo) && timeInfo.length() >= 16) {
            return timeInfo.substring(11, 16);
        } else {
            return "";
        }
    }

}
